Skip to content

fix(bindx-ui): quiet the datagrid cell filter affordance, advertise it in the header - #104

Closed
matej21 wants to merge 2 commits into
mainfrom
fix/datagrid-relation-cell-affordance
Closed

fix(bindx-ui): quiet the datagrid cell filter affordance, advertise it in the header#104
matej21 wants to merge 2 commits into
mainfrom
fix/datagrid-relation-cell-affordance

Conversation

@matej21

@matej21 matej21 commented Sep 9, 2026

Copy link
Copy Markdown
Member

What the affordance signalled vs. what it means

A dashed underline is the <abbr> convention: "this term is special / has a definition", or simply "this is a link". In the datagrid it means something else entirely — "click for filter actions". Every relation and enum cell of every datagrid paid that cost permanently, which is a lot of standing visual noise for a power-user feature.

This moves the permanent "you can filter here" signal to the column header, where it belongs, and keeps the per-cell "filter by this value" shortcut as an on-demand affordance.

The two changes

1. Cell — the decoration is revealed on hover and on keyboard focus (datagrid/ui/label-ui.tsx, ui/tooltip.tsx)

The underline is still there geometrically, just decoration-transparent at rest; it fades in to gray-400 on hover and gray-800 on focus-visible. It stays text-decoration-based rather than border-b, so the wrapped-text fix from 2bbd12f is preserved — a wrapped relation name still underlines per line instead of looking struck through.

The label also gets tabIndex={0} and the tooltip now opens on group-focus-within, so the Filter / Exclude actions are reachable without a mouse. They previously were not reachable at all by keyboard.

2. Header — a muted filter icon on every filterable column (datagrid/column-header.tsx)

DataGridColumnFilterIcon used to render null while the filter was empty, so nothing told an operator which columns can be filtered. It now always renders: text-muted-foreground/50 when idle, unchanged text-blue-600 when a filter is active. That is the same idle-grey / active-blue convention the sorting icons in this header already use.

Verified in the browser

Checked in the example playground (bun run playground), where Author is a has-one relation column. Computed styles, not just intent:

  • At rest: text-decoration: underline dashed rgba(0, 0, 0, 0) — the geometry is kept, the colour is invisible. Cells read as plain text, and no header icon competes with the data.
  • Hover on a relation cell: text-decoration-color resolves to gray-400 and the Filter / Exclude tooltip opens. Neighbouring cells stay quiet.
  • Keyboard, mouse parked away from the table: tabbing reaches the cell, text-decoration-color resolves to gray-800, a 1px --color-ring focus ring appears and the tooltip opens. One more Tab moves into the tooltip's Filter button and Enter applies the filter — the path that did not exist before this change.
  • Headers: the filterable columns (Title, Published, Author, Tags) show the muted icon; Content, which is not filterable, shows none. With a filter applied, that column's icon is blue and clearly distinct from the muted rest.

Two caveats about the verification itself:

  • Enum cells were not seen live — the playground has no enum column. They render through the same DataGridTooltipLabel, so the styling is identical, but that is inference, not observation.
  • Has-many cells never reach DataGridTooltipLabel at all: columns/has-many-column.tsx bails when the related accessor id is null, which for a list is always. That is pre-existing and untouched here, but it means this change visibly affects has-one and enum columns, not all three.

Scope

  • The mechanical half of DataGrid: relation cell affordance is too noisy and breaks on multi-line text #30 — the border-b strikethrough on wrapped text — already landed separately in 2bbd12f. This PR is the UX half.
  • The larger redesign the issue proposes as an alternative (drop the cell decoration entirely and add a per-cell filter icon at the right edge on row hover) was considered and declined. The cheap variant here gets the same "quiet by default, discoverable on interaction" result without a new per-cell control.
  • No test is added. This is pure styling, and a unit test asserting Tailwind class strings would assert the implementation rather than the behaviour. Verified in the browser instead — see the screenshots.

DataGridTooltipLabel is shared by the has-one, has-many and enum cells, so all three change together. That is intended.

Verified: bun run typecheck clean, bun run test 2026 pass / 0 fail.

Fixes #30

🤖 Generated with Claude Code

https://claude.ai/code/session_01Euwkf2wtutqvtE4YRutU5R

matej21 and others added 2 commits September 9, 2026 16:05
…ocus

A permanent dashed underline in every relation and enum cell reads as
"link" or "has a tooltip", while it actually means "click for filter
actions" — permanent noise for a power-user feature.

The decoration stays underline-based, so the wrapped-text fix from
2bbd12f is preserved; only its color is now transparent at rest.
The label becomes focusable and the tooltip opens on focus-within, so
the filter actions are reachable without a mouse.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Euwkf2wtutqvtE4YRutU5R
The header icon used to appear only once a filter was set, so nothing
told an operator which columns can be filtered. It now always renders:
muted when idle, blue when a filter is active.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Euwkf2wtutqvtE4YRutU5R
@matej21

matej21 commented Sep 10, 2026

Copy link
Copy Markdown
Member Author

Consolidated into #109, merged there.

@matej21 matej21 closed this Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DataGrid: relation cell affordance is too noisy and breaks on multi-line text

1 participant